package org.gluedom.transport;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.gluedom.data.Immutable;
import org.gluedom.marshall.Marshaller;
import org.gluedom.marshall.Marshallers;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.MapMaker;

public class Transports {
	private Transports() {
		throw new AssertionError();
	}

	private static final Map<Class<? extends TransportFactory>, TransportFactory> factoryTable = new MapMaker()
			.makeMap();
	private static final ExecutorService executor = Executors
			.newCachedThreadPool();
	private static String marshaller = Marshallers.DEFAULT;
	public static final long DEFAULT_TIMEOUT = 250;
	public static final TimeUnit DEFAULT_UNIT = TimeUnit.MILLISECONDS;

	static {
		factoryTable.put(MemoryTransportFactory.class,
				new MemoryTransportFactory());
	}

	public static <T extends TransportFactory> void registerFactory(
			Class<T> clazz, T factory) {
		factoryTable.put(clazz, factory);
	}

	public static Transport getTransport(Connection connection) {
		checkNotNull(connection);
		return factoryTable.get(connection.getFactoryClass()).getInstance(
				connection);
	}

	public static void setMarshaller(String marshaller) {
		checkNotNull(marshaller);
		Transports.marshaller = marshaller;
	}

	public static Marshaller getMarshaller() {
		return Marshallers.getMarshaller(marshaller);
	}

	public static ImmutableList<Immutable> sendSync(Connection connection,
			ImmutableList<Immutable> data) {
		return sendSync(connection, data, DEFAULT_TIMEOUT, DEFAULT_UNIT);
	}

	public static ImmutableList<Immutable> sendSync(Connection connection,
			ImmutableList<Immutable> data, long timeout, TimeUnit unit) {
		try {
			return executor.invokeAny(Arrays
					.asList(new ConnectionCallable[] { new ConnectionCallable(
							connection, data) }), timeout, unit);
		} catch (InterruptedException e) {
			return null;
		} catch (ExecutionException e) {
			throw new IllegalStateException(
					"exception in synchronous transport", e.getCause());
		} catch (TimeoutException e) {
			return null;
		}
	}
}